#!/usr/bin/env python3
"""
OPC UA节点配置助手
帮助用户快速配置正确的OPC UA节点ID
"""

import json
import sys
import os
import asyncio

# 添加项目根目录到Python路径
sys.path.insert(0, os.path.dirname(__file__))

try:
    from asyncua import Client
    OPCUA_AVAILABLE = True
except ImportError:
    OPCUA_AVAILABLE = False

class OPCUANodeHelper:
    def __init__(self):
        self.config_file = "config/devices.json"
        
    def load_devices(self):
        """加载设备配置"""
        try:
            with open(self.config_file, 'r', encoding='utf-8') as f:
                return json.load(f)
        except FileNotFoundError:
            print("❌ 未找到设备配置文件")
            return None
        except Exception as e:
            print(f"❌ 读取配置文件失败: {e}")
            return None
    
    def save_devices(self, devices_config):
        """保存设备配置"""
        try:
            with open(self.config_file, 'w', encoding='utf-8') as f:
                json.dump(devices_config, f, indent=2, ensure_ascii=False)
            return True
        except Exception as e:
            print(f"❌ 保存配置文件失败: {e}")
            return False
    
    def find_opcua_devices(self, devices_config):
        """查找OPC UA设备"""
        if not devices_config:
            return []
        
        opcua_devices = []
        for i, device in enumerate(devices_config.get('devices', [])):
            if device.get('type') == 'opcua':
                opcua_devices.append((i, device))
        return opcua_devices
    
    async def browse_server_nodes(self, endpoint):
        """浏览OPC UA服务器节点"""
        if not OPCUA_AVAILABLE:
            print("❌ asyncua库未安装，请运行: pip install asyncua")
            return []
        
        client = Client(url=endpoint)
        nodes_found = []
        
        try:
            await client.connect()
            print(f"✅ 连接成功: {endpoint}")
            
            # 搜索常见的温度、频率等节点
            search_keywords = ['temp', 'freq', 'speed', 'current', 'voltage', 'pressure', 'flow']
            
            await self._search_nodes_recursive(client, client.get_objects_node(), 
                                             search_keywords, nodes_found, depth=0, max_depth=4)
            
        except Exception as e:
            print(f"❌ 浏览节点失败: {e}")
        finally:
            await client.disconnect()
        
        return nodes_found
    
    async def _search_nodes_recursive(self, client, node, keywords, results, depth=0, max_depth=3):
        """递归搜索节点"""
        if depth > max_depth:
            return
        
        try:
            children = await node.get_children()
            
            for child in children:
                try:
                    display_name = await child.read_display_name()
                    browse_name = await child.read_browse_name()
                    node_id = child.nodeid
                    
                    # 检查是否包含关键词
                    name_text = display_name.Text.lower()
                    browse_text = browse_name.Name.lower()
                    
                    for keyword in keywords:
                        if keyword in name_text or keyword in browse_text:
                            try:
                                # 尝试读取值判断是否为变量节点
                                value = await child.read_value()
                                results.append({
                                    'display_name': display_name.Text,
                                    'browse_name': browse_name.Name,
                                    'node_id': str(node_id),
                                    'value': str(value),
                                    'keyword_matched': keyword
                                })
                            except:
                                # 不是变量节点，继续搜索
                                pass
                            break
                    
                    # 继续递归搜索
                    if depth < max_depth:
                        await self._search_nodes_recursive(client, child, keywords, results, depth + 1, max_depth)
                        
                except Exception:
                    # 忽略无法访问的节点
                    continue
                    
        except Exception:
            # 忽略无法浏览的节点
            pass
    
    def configure_device_points(self, device_index, devices_config, suggested_nodes):
        """配置设备数据点"""
        device = devices_config['devices'][device_index]
        
        print(f"\n🔧 配置设备: {device['name']}")
        print(f"端点: {device['config']['endpoint']}")
        
        if not suggested_nodes:
            print("❌ 未找到建议的节点，请手动配置")
            return False
        
        print(f"\n📋 找到 {len(suggested_nodes)} 个可能的节点:")
        for i, node in enumerate(suggested_nodes):
            print(f"  {i+1}. {node['display_name']} (匹配: {node['keyword_matched']})")
            print(f"      节点ID: {node['node_id']}")
            print(f"      当前值: {node['value']}")
            print()
        
        # 自动配置H1SP1_ATemp节点
        h1sp1_node = None
        for node in suggested_nodes:
            if 'H1SP1' in node['display_name'] and 'temp' in node['keyword_matched']:
                h1sp1_node = node
                break
        
        if h1sp1_node:
            print("🎯 自动配置H1SP1_ATemp节点")
            
            # 更新设备配置
            points = device.get('points', [])
            
            # 查找现有的H1SP1_ATemp点
            h1sp1_point = None
            for point in points:
                if point.get('name') == 'H1SP1_ATemp':
                    h1sp1_point = point
                    break
            
            if h1sp1_point:
                # 更新现有点
                h1sp1_point['node_id'] = h1sp1_node['node_id']
                h1sp1_point['description'] = '1#混流泵A相绕组温度'
                h1sp1_point['unit'] = '°C'
                print(f"✅ 更新现有数据点: {h1sp1_point['name']}")
            else:
                # 添加新点
                new_point = {
                    'name': 'H1SP1_ATemp',
                    'description': '1#混流泵A相绕组温度',
                    'data_type': 'float',
                    'access': 'R',
                    'unit': '°C',
                    'node_id': h1sp1_node['node_id']
                }
                points.append(new_point)
                print(f"✅ 添加新数据点: {new_point['name']}")
            
            device['points'] = points
            return True
        
        return False
    
    def print_device_summary(self, devices_config):
        """打印设备配置摘要"""
        opcua_devices = self.find_opcua_devices(devices_config)
        
        print("\n📊 OPC UA设备配置摘要:")
        print("=" * 50)
        
        for i, (device_index, device) in enumerate(opcua_devices):
            print(f"\n{i+1}. 设备: {device['name']}")
            print(f"   端点: {device['config']['endpoint']}")
            print(f"   启用: {'是' if device.get('enabled') else '否'}")
            
            points = device.get('points', [])
            print(f"   数据点数量: {len(points)}")
            
            for point in points:
                print(f"     ├─ {point['name']}: {point.get('description', '')}")
                if 'node_id' in point:
                    print(f"     │   节点ID: {point['node_id']}")
                else:
                    print(f"     │   ⚠️  缺少节点ID")
                print(f"     │   类型: {point.get('data_type', 'float')} | 权限: {point.get('access', 'R')}")
    
    async def auto_configure(self):
        """自动配置助手"""
        print("🚀 OPC UA节点配置助手")
        print("=" * 40)
        
        # 1. 加载配置
        devices_config = self.load_devices()
        if not devices_config:
            return
        
        # 2. 查找OPC UA设备
        opcua_devices = self.find_opcua_devices(devices_config)
        if not opcua_devices:
            print("❌ 未找到OPC UA设备配置")
            return
        
        print(f"📍 找到 {len(opcua_devices)} 个OPC UA设备")
        
        # 3. 处理每个OPC UA设备
        config_updated = False
        for device_index, device in opcua_devices:
            print(f"\n🔍 处理设备: {device['name']}")
            endpoint = device['config'].get('endpoint')
            
            if not endpoint:
                print("❌ 设备缺少端点配置")
                continue
            
            # 4. 浏览节点
            print(f"🔍 浏览OPC UA服务器节点...")
            suggested_nodes = await self.browse_server_nodes(endpoint)
            
            # 5. 配置数据点
            if self.configure_device_points(device_index, devices_config, suggested_nodes):
                config_updated = True
        
        # 6. 保存配置
        if config_updated:
            if self.save_devices(devices_config):
                print("\n✅ 配置已保存到 config/devices.json")
                
                # 更新时间戳
                import datetime
                devices_config['last_updated'] = datetime.datetime.now().isoformat()
                self.save_devices(devices_config)
                
            else:
                print("\n❌ 保存配置失败")
        
        # 7. 显示摘要
        self.print_device_summary(devices_config)

async def main():
    """主函数"""
    helper = OPCUANodeHelper()
    await helper.auto_configure()

if __name__ == "__main__":
    asyncio.run(main()) 